#include "parse_genomic_intervals.h"


#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/home/phoenix/core/argument.hpp>
#include <boost/spirit/home/phoenix/algorithm.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/phoenix/object/construct.hpp>
#include <boost/tr1/tuple.hpp>

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

using std::string;
using std::cerr;
using std::vector;

#include "qi_parse_error_handler.h"

//______________________________________________________________________________
// 
//  genomic_interval
// 
//______________________________________________________________________________
BOOST_FUSION_ADAPT_STRUCT(
t_genomic_interval,
(std::string, contig)
(unsigned, beg)
(unsigned, end)
(std::string, name)
)



phoenix::function<errorHandlerT> const errorHandler_genomic_intervals_handler = errorHandlerT("Genomic Intervals");



template <typename Iterator>
struct genomic_interval_grammar
: qi::grammar<Iterator, t_genomic_interval()>
{
    genomic_interval_grammar(unsigned& cnt_line_, bool named_intervals)
    : genomic_interval_grammar::base_type(genomic_interval), cnt_line(cnt_line_)
    {
        using qi::lit;
        using ascii::char_;
        using qi::int_;
        using namespace qi::labels;
        using qi::eol;

        contig                %= -lit("chr") > +(char_ - '\t'); 
        beg                   %= int_;
		end                   %= int_;
		tab			          =  lit('\t');
		interval_name         =  +(char_ - '\t');

		if (named_intervals)
			genomic_interval %=     contig > tab > beg > tab > end	> tab > interval_name;
		else
			genomic_interval %=     contig > tab > beg > tab > end	;
		contig.name("contig/chromosome");
		beg.name("begin");
		end.name("end");
		tab.name("TAB");
		interval_name.name("Interval name");

		qi::on_error<qi::fail>                               
        (genomic_interval, ::errorHandler_genomic_intervals_handler(_1, _2, _3, _4, phoenix::ref(cnt_line), phoenix::ref(error_msg), true));
    }
	string error_msg;
    unsigned& cnt_line;
    qi::rule<Iterator, string()>     			contig;
    qi::rule<Iterator, int()>             		beg;
	qi::rule<Iterator, int()>             		end;
	qi::rule<Iterator, string()>     			interval_name;
    qi::rule<Iterator, void()>            		tab;
    qi::rule<Iterator, t_genomic_interval()> 	genomic_interval;
};


sp_genomic_interval_grammar
create_genomic_interval_grammar(unsigned& curr_line_number, bool named_intervals)
{
	return sp_genomic_interval_grammar(new genomic_interval_grammar<string::const_iterator>(curr_line_number, named_intervals));
}


bool parse_genomic_interval(sp_genomic_interval_grammar p_genomic_interval_grammar, std::string::const_iterator& str_begin, std::string::const_iterator str_end, t_genomic_interval& res)
{
	genomic_interval_grammar<string::const_iterator>& genomic_interval_grammar = *p_genomic_interval_grammar;
    return qi::parse(str_begin, str_end, genomic_interval_grammar, res);
}
    










//______________________________________________________________________________
// 
//  genomic_interval_by_type
// 
//______________________________________________________________________________
BOOST_FUSION_ADAPT_STRUCT(
t_genomic_interval_by_type,
(std::string, interval_type)
(std::string, contig)
(unsigned, beg)
(unsigned, end)
)


phoenix::function<errorHandlerT> const errorHandler_genomic_interval_by_types_handler = errorHandlerT("Genomic Intervals by type");



template <typename Iterator>
struct genomic_interval_by_type_grammar
: qi::grammar<Iterator, t_genomic_interval_by_type()>
{
    genomic_interval_by_type_grammar(unsigned& cnt_line_)
    : genomic_interval_by_type_grammar::base_type(genomic_interval_by_type), cnt_line(cnt_line_)
    {
        using qi::lit;
        using ascii::char_;
        using qi::int_;
        using namespace qi::labels;
        using qi::eol;

		interval_type		%= +(char_ - '\t'); 
		contig              %= -lit("chr") > +(char_ - '\t'); 
        beg                 %= int_;
		end                 %= int_;
		tab			        =  lit('\t');

        genomic_interval_by_type %=     interval_type > tab > contig > tab > beg > tab > end	;
		interval_type.name("interval class");
		contig.name("contig/chromosome");
		beg.name("begin");
		end.name("end");
		tab.name("TAB");

		qi::on_error<qi::fail>                               
        (genomic_interval_by_type, ::errorHandler_genomic_interval_by_types_handler(_1, _2, _3, _4, phoenix::ref(cnt_line), phoenix::ref(error_msg), true));
    }
    unsigned& cnt_line;
	string error_msg;
	qi::rule<Iterator, string()>     					interval_type;
    qi::rule<Iterator, string()>     					contig;
    qi::rule<Iterator, int()>             				beg;
	qi::rule<Iterator, int()>             				end;
    qi::rule<Iterator, void()>            				tab;
    qi::rule<Iterator, t_genomic_interval_by_type()> 	genomic_interval_by_type;
};


sp_genomic_interval_by_type_grammar
create_genomic_interval_by_type_grammar(unsigned& curr_line_number)
{
	return sp_genomic_interval_by_type_grammar(new genomic_interval_by_type_grammar<string::const_iterator>(curr_line_number));
}


bool parse_genomic_interval_by_type(sp_genomic_interval_by_type_grammar p_genomic_interval_by_type_grammar, const string& str, t_genomic_interval_by_type& res)
{
	string::const_iterator str_beg = str.begin();
	genomic_interval_by_type_grammar<string::const_iterator>& genomic_interval_by_type_grammar = *p_genomic_interval_by_type_grammar;
    return qi::parse(str_beg, str.end(), genomic_interval_by_type_grammar, res);
}
    


